<--- %%NOBANNER%% --> bnmleqv.sas
 BackForward

/*-------------------<-- Start of Description-->---------------------\
| Binomial Equivalence Test;                                         |
|---------------------<-- End of Description-->----------------------|
|--------------------------------------------------------------------|
|-----------<-- Start of Files or Arguements Needed-->---------------|
| Arguments:                                                         |
|    This fuction take either variables from a dataset or direct     |
|    numbers;                                                        |
|    indata: input data set;                                         |
|    ne: experiment group;                                           |
|    pe: success rate in experiment group;                           |
|    se: count of success in experiment group;                       |
|    nc: control group;                                              |
|    pc: success rate in control group;                              |
|    sc: count of success in control group;                          |
|    delta: = critical difference:                                   |
|        H0: (Control group - Experimental Group)=delta;             |
|        default is 0;                                               |
|    tail: = One Sided Test or Two Sided Test;                       |
|    better:= the bigger the proportion the better, or the smaller   |
|             the better; BIG or SMALL: default is BIG;              |
|             Note: this option is required for One sided test;      |
|    test: HA (stands for Hauck-Anderson), BW (for Blackwelder), FM  |
|          (for Farrington Manning Procedure);                       |
|          default is HA;                                            |
|          Note: the HA procedure One-Sided test will be the same as |
|                BW procedure.                                       |
|    outdata: output dataset;                                        |
|------------<-- End of Files or Arguements Needed-->----------------|
|--------------------------------------------------------------------|
|------------------<-- Start of Files Created-->---------------------|
| Example: %bnmleqv(ne=50, se=40, nc=60, sc=49,delta=0.10, test=FM,  |
|                   tail=two, outdata=one);                          |
| Example 2:                                                         |
|  data one;                                                         |
|     success_e=70;                                                  |
|     n_e=100;                                                       |
|     success_c=49;                                                  |
|     n_c=60;                                                        |
|  run;                                                              |
|  %bnmleqv(indata=one, ne=n_e, se=success_e, nc=n_c, sc=success_c,  |
|           test=fm, better=small, alpha=0.05, delta=0.1,tail=one);  |
| Usage:   %bnmleqv(indata=, ne=, pe=, se=, nc=, pc=, sc=, tail=one, |
|                   outdata=);                                       |
\------------------<-- Start of Files Created-->--------------------*/
%macro bnmleqv(indata=, ne=, pe=, se=, nc=, pc=, sc=, delta=0, tail=one,
               better=big,alpha=0.05,test=, outdata=);
/*--------------------------------------------\
| Author:  Duo Zhou, Brian Johnson;           |
| Created: 2-1-2002 7:02pm;                   |
| Purpose: Binomial Equivalence Test;         |
\--------------------------------------------*/
%local _nnumeric_ _pnumeric_ _snumeric_ _consistence_ _i_ _grp_1 _grp_2 _linesize_ _tmplast_;
%let _tmplast_=&syslast;
%local _n1 _p1 _s1 _n2 _p2 _s2;
%let _grp_1=experimental;
%let _grp_2=control;
%let _linesize_ = %SYSFUNC(GETOPTION(linesize));
%let _n1=≠ %let _p1=&pe; %let _s1=&se; %let _n2=&nc; %let _p2=&pc; %let _s2=≻
%if (%quote(%upcase(&tail)) = ONE) or (%quote(%upcase(&tail)) = 1) %then %do;
   %if (not %index(%quote(%upcase(&better)), BIG)) and (not %index(%quote(%upcase(&better)), SMALL)) %then %do;
      %put ==> Alert! In order to setup the hypothesis, I need to know if the bigger the proportion;
      %put +++        the better, or the smaller the proportions the better. Please specify a valid;
      %put +++        option "BIGGER" or "SMALLER"!;
   %end;
%end;
data _tmp1_;
   retain _hyp0 hyp1 _diffSE _tvalue _pvalue;
   %if &indata ne %then %do;
   set &indata;
   %end;
   length _hyp0 _hyp1 $200.;
   format _n1 _n2 _s1 _s2 Best10. default= 11.8;
   %do _i_=1 %to 2;
      %if (%quote(&&_n&_i_) ne) and (%quote(&&_p&_i_) ne) and (%quote(&&_s&_i_) ne) %then %do;
         %if (%sysevalf(%sysevalf(&&_n&_i_*&&_p&_i_)-&&_s&_i_)>=0.5) %then %do;
            %put ==> Alert! Inconsistency: (&&_n&_i_*&&_p&_i_ <> &&_s&_i_).;
            %goto finish;
         %end;
         _n%trim(%left&_i_=&&_n&_i_;
         _p&_i_=&&_p&_i_;
         _s&_i_=&&_s&_i_;
      %end;
      %else %if (%quote(&&_n&_i_) ne) and (%quote(&&_p&_i_) ne) and (%quote(&&_s&_i_) eq) %then %do;
         _n&_i_=&&_n&_i_;
         _p&_i_=&&_p&_i_;
         _s&_i_=(_n&_i_)*(_p&_i_);
      %end;
      %else %if (%quote(&&_n&_i_) ne) and (%quote(&&_p&_i_) eq) and (%quote(&&_s1) ne) %then %do;
         _n&_i_=&&_n&_i_;
         _s&_i_=&&_s&_i_;
         _p&_i_=(_s&_i_)/(_n&_i_);
      %end;
      %else %if (%quote(&&_n&_i_) eq) and (%quote(&&_p&_i_) ne) and (%quote(&&_s1) ne) %then %do;
         _p&_i_=&&_p&_i_;
         _s&_i_=&&_s&_i_;
         _n&_i_=(_s&_i_)/(_p&_i_);
      %end;
      %else %do;
         %put ==> Alert! I will need at least 2 of the three parameters:_n&_i_,_p&_i_ and _s&_i_;
         %put +++        in order to proceed.
         %goto finish;
      %end;
   %end;
   _p=(_s1+_s2)/(_n1+_n2);
   %if (%quote(&test) eq) or (%quote(%upcase(&test)) eq %quote(BW)) or
       %index(%quote(%upcase(&test)),%quote(BLACKWELDER)) or
       (%index(%quote(%upcase(&test)),HA)) or
       (%index(%quote(%upcase(&test)),%quote(HAUCK)) and
       %index(%quote(%upcase(&test)),%quote(ANDERSON)))%then %do;
      %if ((%index(%quote(%upcase(&test)),HA)) or (%index(%quote(%upcase(&test)),%quote(HAUCK))
           and %index(%quote(%upcase(&test)),%quote(ANDERSON)))) and
          ((%index(%quote(%upcase(&tail)),TWO)) or (%quote(&tail) eq 2)) %then %do;
            _diffSE=SQRT(_p1*(1-_p1)/(_n1-1)+_p2*(1-_p2)/(_n2-1));
      %end;
      %else %do;
         %if (%quote(&delta) eq) or (%quote(&delta) eq %quote(0)) %then %do;
            %let delta=0;
            _diffSE=SQRT(_p*(1-_p)*(1/_n1+1/_n2));
         %end;
         %else %do;
            _diffSE=SQRT(_p1*(1-_p1)/_n1+_p2*(1-_p2)/_n2);
         %end;
      %end;
      _c=1/(2*min(_n1, _n2));
      %if (%index(%quote(%upcase(&tail)),TWO)) or (%quote(&tail) eq 2) %then %do;
      _hyp0="H0: |pc-pe|<=%trim(%left(&delta))"; _hyp1="Ha: |pc-pe| > %trim(%left(&delta))";
         %if (%index(%quote(%upcase(&test)),HA)) or (%index(%quote(%upcase(&test)),%quote(HAUCK))
              and %index(%quote(%upcase(&test)),%quote(ANDERSON))) %then %do;
            _ucl = (_p1-_p2) + probit(1 - &alpha) * _diffSE + _c;
            _lcl = (_p1-_p2) - probit(1 - &alpha) * _diffSE - _c;
            if (1-probnorm((_p1-_p2+&delta-_c)/_diffSE)) >=(1-probnorm(abs((_p1-_p2-&delta+_c)/_diffSE)))
               then _tvalue = (_p1-_p2+&delta-_c)/_diffSE;
            else _tvalue=(_p1-_p2-&delta+_c)/_diffSE;
            _pvalue=max((1-probnorm(abs(_p1-_p2+&delta-_c)/_diffSE)),(1-probnorm(abs((_p1-_p2-&delta+_c)/_diffSE))));
         %end;
         %else %do;
            _lcl = (_p1-_p2) - probit(1 - &alpha/2) * _diffSE;
            _ucl = (_p1-_p2) + probit(1 - &alpha/2) * _diffSE;
            _tvalue = (_p1-_p2+&delta)/_diffSE;
            _pvalue=(1-probnorm(abs((_p1-_p2+&delta)/_diffSE)))*2;
         %end;
         keep _lcl _ucl;
      %end;
      %else %if (%index(%quote(%upcase(&tail)),ONE)) or (%quote(&tail) eq 1) %then %do;
         %if (%index(%quote(%upcase(&better)), BIG)) %then %do;
         _hyp0="H0: pe <= pc-%trim(%left(&delta))"; _hyp1="Ha: pe > pc-%trim(%left(&delta))";
         _tvalue=(_p1-_p2+&delta)/_diffSE;
         _lcl = (_p1-_p2) - probit(1 - &alpha) * _diffSE;
         _pvalue=(1-probnorm(_tvalue));
         keep _lcl;
         %end;
         %else %if (%index(%quote(%upcase(&better)), SMALL)) %then %do;
         _hyp0="H0: pe >= pc+%trim(%left(&delta))"; _hyp1="Ha: pe < pc+%trim(%left(&delta))";
         _tvalue=(_p1-_p2-&delta)/_diffSE;
         _ucl = (_p1-_p2) + probit(1 - &alpha) * _diffSE;
         _pvalue=probnorm(_tvalue);
         keep _ucl;
         %end;
      %end;
   %end;
   %else %if (%index(%quote(%upcase(&test)),FM)) or
             (%index(%quote(%upcase(&test)),%quote(FARRINGTON)) and
              %index(%quote(%upcase(&test)),%quote(MANNING))) %then %do;
      %if (%quote(&delta) eq) or (%quote(&delta) eq %quote(0)) %then %do;
         %let delta=0;
      %end;
      format _pi 11.8;
      _pi = arcos(-1);
      /* Note: the following is for "The smaller, the better"*/
      /* Note: here _n1 and _n2 are all swapped from the original paper */
      _theta1=_n2/_n1;
      _a1 = 1 + _theta1;
      _b1 =  - (1 + _theta1 + _p1 + _theta1 * _p2 + &delta * (_theta1 + 2));
      _c1 = &delta**2 + &delta * (2 * _p1 + _theta1 + 1) + _p1 + _theta1 * _p2;
      _d1 =  -_p1 * &delta * (1 + &delta);
      _v1 = (_b1/(3*_a1))**3 - (_b1*_c1)/(6*(_a1**2)) + _d1/(2*_a1);
      _u1 = sign(_v1)*sqrt((_b1/(3 * _a1))**2 - _c1/(3 * _a1));
      _w1 = (1/3) * (_pi + arcos(_v1/_u1**3));
      _p1_est1 = 2 * _u1 * cos(_w1) - _b1/(3 * _a1);
      _p2_est1 = _p1_est1 - δ
      _diffSE1=sqrt((_p1_est1 * (1 - _p1_est1) + (_p2_est1 * (1 - _p2_est1))/_theta1)/_n1);
      /* Note: here _n1 and _n2 are all swapped from the original paper
               for "The bigger, the better";
      */
      _theta2=_n1/_n2;
      _a2 = 1 + _theta2;
      _b2 =  - (1 + _theta2 + _p2 + _theta2 * _p1 + &delta * (_theta2 + 2));
      _c2 = &delta**2 + &delta * (2 * _p2 + _theta2 + 1) + _p2 + _theta2 * _p1;
      _d2 =  -_p2 * &delta * (1 + &delta);
      _v2 = (_b2/(3*_a2))**3 - (_b2*_c2)/(6*(_a2**2)) + _d2/(2*_a2);
      _u2 = sign(_v2)*sqrt((_b2/(3 * _a2))**2 - _c2/(3 * _a2));
      _w2 = (1/3) * (_pi + arcos(_v2/_u2**3));
      _p2_est2 = 2 * _u2 * cos(_w2) - _b2/(3 * _a2);
      _p1_est2 = _p2_est2 - δ
      _diffSE2=sqrt((_p2_est2 * (1 - _p2_est2) + (_p1_est2 * (1 - _p1_est2))/_theta2)/_n2);
      %if (%index(%quote(%upcase(&tail)),TWO)) or (%quote(&tail) eq 2) %then %do;
      _hyp0="H0: |pc-pe|<=%trim(%left(&delta))"; _hyp1="Ha: |pc-pe| > %trim(%left(&delta))";
      if (1-probnorm(abs((_p1-_p2+&delta)/_diffSE2)))>=(1-probnorm(abs((_p1-_p2-&delta)/_diffSE2))) then do;
         _diffSE=_diffSE2;
         _tvalue = (_p1-_p2+&delta)/_diffSE;
      end;
      else do;
         _diffSE=_diffSE1; put _diffse1=;
         _tvalue=(_p1-_p2-&delta)/_diffSE;
      end;
      _pvalue=(1-probnorm(abs((_p1-_p2+&delta)/_diffSE2)))+(1-probnorm(abs((_p1-_p2-&delta)/_diffSE1)));
      _lcl = (_p1-_p2) - probit(1 - &alpha/2) * _diffSE;
      _ucl = (_p1-_p2) + probit(1 - &alpha/2) * _diffSE;
      keep _lcl _ucl;
      %end;
      %else %if (%index(%quote(%upcase(&tail)),ONE)) or (%quote(&tail) eq 1) %then %do;
         %if (%index(%quote(%upcase(&better)), BIG)) %then %do;
         _hyp0="H0: pe <= pc-%trim(%left(&delta))"; _hyp1="Ha: pe > pc-%trim(%left(&delta))";
         _diffSE=_diffSE2;
         _tvalue = (_p1-_p2+&delta)/_diffSE;
         _lcl = (_p1-_p2) - probit(1 - &alpha) * _diffSE;
         _pvalue=(1-probnorm(_tvalue));
         keep _lcl;
         %end;
         %else %if (%index(%quote(%upcase(&better)), SMALL)) %then %do;
         _hyp0="H0: pe >= pc+%trim(%left(&delta))"; _hyp1="Ha: pe < pc+%trim(%left(&delta))";
         _diffSE=_diffSE1;
         _tvalue = (_p1-_p2-&delta)/_diffSE1;
         _ucl = (_p1-_p2) + probit(1 - &alpha) * _diffSE;
         _pvalue=probnorm(_tvalue);
         keep _ucl;
         %end;
      %end;
   %end;
   %else %do;
      %put ==> Alert! I don%str(%')t recognize "&test"! I can only run "HA" (stand for "HAUCK ANDERSON") or;
      %put +++        "FM" (stand for "FARRINGTON MANNING"). Please choose one test!;
      %goto finish;
   %end;
   keep _p _tvalue _diffSE _hyp0 _hyp1 _pvalue;
   label _tvalue="Test Statistics" _diffSE="Standard Error of the Proportion Difference"
         _hyp0="Null Hypothesis" _hyp1="Alternative Hypothesis" _p="Overall Success Rate"
         _n1="Sample size of Control Group" _p1="Success Rate in Control Group"
         _n2="Sample size of Experimental Group" _p2="Success Rate in Experimental Group"
         _s1="Number of Successes in Control Group" _s2="Number of Successes in Experimental Group"
         _pvalue="P-Value" _lcl="Lower CI for (pc-pe)" _ucl="Upper CI for (pc-pe)";
run;
data %if (%quote(&outdata) ne) %then %do; &outdata %end; %else %do; %let syslast=&_tmplast_; _null_ %end;;
   file print;
   set _tmp1_;
   format default=11.8;
   %if (%quote(&test) eq) or (%quote(%upcase(&test)) eq %quote(BW)) or
       (%index(%quote(%upcase(&test)),%quote(BLACKWELDER))) or
       (%index(%quote(%upcase(&test)),HA)) or
       (%index(%quote(%upcase(&test)),%quote(HAUCK)) and
        %index(%quote(%upcase(&test)),%quote(ANDERSON))) %then %do;
      put /;
      %if (%index(%quote(%upcase(&tail)),TWO)) or (%quote(&tail) eq 2) %then %do;
         %if (%index(%quote(%upcase(&test)),HA)) or (%index(%quote(%upcase(&test)),%quote(HAUCK)) and
              %index(%quote(%upcase(&test)),%quote(ANDERSON))) %then %do;
         put /@5 "Two-Sided Hypothesis Test on Two Proportions Using Normal Approximation with";
         put  @5 "Hauck-Anderson Correction";
         put  @5 76*"="/;
         put @5 "Reference: Hauck w. w. and Andersen S.A. (1986) A comparison of large-sample"/
             @5 "confidence interval methods for the difference of two binomial probabilities."/
             @5 "AM Stat. VOL 40, 318-322.";
         %end;
         %else %do;
         put /@5 "Two-Sided Hypothesis Test on Two Proportions Using Normal Approximation with";
         put  @5 "Blackwelder Procedure";
         put  @5 76*"="/;
         put  @5 "Reference: Blackwelder, W. C. (1982). Proving the null hypothesis in clinical"/
              @5 "trials. Controlled Clinical Trials 3, 345-353.;";
         %end;
      %end;
      %else %if ((%index(%quote(%upcase(&tail)),ONE)) or (%quote(&tail) eq 1)) %then %do;
         put /@5 "One-Sided Hypothesis Test on Two Proportions Using Normal Approximation with";
         put  @5 "Blackwelder Procedure";
         put  @5 76*"="/;
         put  @5 "Reference: Blackwelder, W. C. (1982). Proving the null hypothesis in clinical"/
              @5 "trials. Controlled Clinical Trials 3, 345-353.;";
      %end;
   %end;
   %else %if (%index(%quote(%upcase(&test)),FM)) or (%index(%quote(%upcase(&test)),%quote(FARRINGTON MANNING)))%then %do;
      %if (%index(%quote(%upcase(&tail)),TWO)) or (%quote(&tail) eq 2) %then %do;
         put /@5 "Two-Sided Hypothesis Test on Two Proportions using Farrington Manning Procedure";
         put  @5 79*"="/;
      %end;
      %else %if ((%index(%quote(%upcase(&tail)),ONE)) or (%quote(&tail) eq 1)) %then %do;
         put /@5 "One-Sided Hypothesis Test on Two Proportions using Farrington Manning Procedure";
         put  @5 79*"="/;
      %end;
      put @5 "Reference: Farrington, C. P. and Manning, G. (1990) Test statistics and sample"/
          @5 "size formulae for comparative binomial trials with null typothesis of non-zero"/
          @5 "risk difference or non-unity relative risk. Statistics in Medicine, VOL 9," /
          @5 "1447-1454.";
   %end;
   put /;
   put &_linesize_*"-";
   %if (%index(%quote(%upcase(&tail)),TWO)) or (%quote(&tail) eq 2) %then %do;
   put @5 "Hypothesis Test (Two Sided Test): ";
   put @10 _hyp0  "vs "  _hyp1 +(-1) ";"/;
   %end;
   %else %if ((%index(%quote(%upcase(&tail)),ONE)) or (%quote(&tail) eq 1)) and (%index(%quote(%upcase(&better)), BIG)) %then %do;
   put @5 "Hypothesis Test (The Bigger, The Better!): ";
   put @10 _hyp0  "vs "  _hyp1 +(-1) ";"/;
   %end;
   %else %if ((%index(%quote(%upcase(&tail)),ONE)) or (%quote(&tail) eq 1)) and (%index(%quote(%upcase(&better)), SMALL))%then %do;
   put @5 "Hypothesis Test (The Smaller, The Better!): ";
   put @10 _hyp0  "vs "  _hyp1 +(-1) ";"/;
   %end;
   put @5 "Overall Success Rate: " @35 _p 20.6  "; ";
   put @5 "Standard Error (Diff): " @35 _diffSE 20.6  "; ";
   put @5 "Test Statistics: " @35 _tvalue 20.6  "; ";
   put @5 "P Value: " @35 _pvalue 20.6  "; "/;
   put &_linesize_*"-"/;
   %if (%index(%quote(%upcase(&tail)),TWO)) or (%quote(&tail) eq 2) %then %do;
      put @5 "Two Sided Confidence Intervals:"/;
      put @5 "%sysevalf((1-&alpha)*100)% CI for (pe-pc): (" _lcl +(-1) ", " _ucl +(-1) ").";
   %end;
   %else %if ((%index(%quote(%upcase(&tail)),ONE)) or (%quote(&tail) eq 1)) %then %do;
      put @5 "One Sided Confidence Interval (Please choose one):"/;
      %if (%index(%quote(%upcase(&better)), BIG)) %then %do;
      put @5 "%sysevalf((1-&alpha)*100)% CI for (pe-pc): (" _lcl +(-1) ", +Inf).";
      %end;
      %else %if (%index(%quote(%upcase(&better)), SMALL)) %then %do;
      put @5 "%sysevalf((1-&alpha)*100)% CI for (pe-pc): (-Inf, " _ucl +(-1) ").";
      %end;
   %end;
run;
proc datasets library=work nolist;
     delete _tmp1_;
run;quit;
%finish:
run; quit;
%mend bnmleqv;